package top.jiangqiang.qyblog.core.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.text.PasswdStrength;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.jiangqiang.qyblog.common.base.query.PageResult;
import top.jiangqiang.qyblog.common.base.response.Result;
import top.jiangqiang.qyblog.common.config.GlobalProperties;
import top.jiangqiang.qyblog.common.exception.JsonException;
import top.jiangqiang.qyblog.core.base.service.impl.BaseServiceImpl;
import top.jiangqiang.qyblog.core.domain.dto.*;
import top.jiangqiang.qyblog.core.domain.entity.AdminUser;
import top.jiangqiang.qyblog.core.domain.vo.AdminUserVo;
import top.jiangqiang.qyblog.core.mapper.AdminUserMapper;
import top.jiangqiang.qyblog.core.service.IAdminUserService;
import top.jiangqiang.qyblog.core.util.SessionUtil;

import java.util.List;
import java.util.Set;

/**
 * @author Jiangqiang
 * @version 1.0
 * @date 2022/11/14 15:27
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class AdminUserServiceImpl extends BaseServiceImpl<AdminUserMapper, AdminUser> implements IAdminUserService {
    private final SecureService secureService;

    @Override
    public AdminUser findByUsername(String username) {
        return lambdaQuery().eq(AdminUser::getUsername, username).one();
    }

    @Override
    public Result<String> login(AdminUserLoginDto adminUserLoginDto) {
        AdminUser adminUser = findByUsername(adminUserLoginDto.getUsername());
        if (adminUser == null) {
            log.debug("登录失败[{}]", adminUserLoginDto.getUsername());
            throw new JsonException("用户不存在");
        } else {
            if (secureService.encryptHex(adminUserLoginDto.getPassword()).equals(adminUser.getPassword())) {
                StpUtil.login(adminUser.getId());
                SessionUtil.setUser(adminUser);
                log.debug("登录成功[{}]", adminUserLoginDto.getUsername());
                return Result.ok(StpUtil.getTokenInfo().getTokenValue());
            } else {
                log.debug("登录失败[{}]", adminUserLoginDto.getUsername());
                return Result.error("用户名或密码错误");
            }
        }
    }

    @Override
    public Result<AdminUserVo> getUserInfo() {
        AdminUser user = SessionUtil.getUser();
        return Result.ok(typeConverter.convert(user, AdminUserVo.class));
    }

    @Override
    public Result<String> registerUser(AdminUserRegisterDto adminUserRegisterDto) {
        long count = count();
        if (count > 0) {
            //限制只能注册一个用户
            return Result.error("当前系统不允许注册多个用户");
        }
        String username = adminUserRegisterDto.getUsername();
        boolean general = validateUsernameRule(username);
        if (!general) {
            throw new JsonException("用户名格式不符合规则");
        }
        AdminUser adminUser = findByUsername(username);
        if (adminUser != null) {
            return Result.error("用户名已被使用");
        }
        if (!adminUserRegisterDto.getPassword().equals(adminUserRegisterDto.getConfirmPassword())) {
            return Result.error("两次密码不一致");
        }
        if (PasswdStrength.getLevel(adminUserRegisterDto.getPassword()).equals(PasswdStrength.PASSWD_LEVEL.EASY)) {
            return Result.error("密码过于简单");
        }
        adminUser = typeConverter.convert(adminUserRegisterDto, AdminUser.class);
        adminUser.setNickname(adminUser.getUsername());
        adminUser.setPassword(secureService.encryptHex(adminUser.getPassword()));
        baseMapper.insert(adminUser);
        //注册成功，自动登录
        StpUtil.login(adminUser.getId());
        SessionUtil.setUser(adminUser);
        log.debug("注册成功[{}]", adminUser.getUsername());
        return Result.ok(StpUtil.getTokenInfo().getTokenValue());
    }

    @Override
    public Result<AdminUserVo> updateUserInfo(AdminUserUpdateInfoDto adminUserUpdateInfoDto) {
        String username = adminUserUpdateInfoDto.getUsername();
        AdminUser adminUser = baseMapper.selectById(adminUserUpdateInfoDto.getId());
        if (adminUser == null) {
            return Result.error("用户不存在");
        }
        if (!adminUser.getUsername().equals(username)) {
            //用户名被修改
            boolean general = validateUsernameRule(username);
            if (!general) {
                throw new JsonException("用户名格式不符合规则");
            }
            AdminUser byUsername = findByUsername(username);
            if (byUsername != null) {
                return Result.error("用户名已被使用");
            }
        }
        adminUser = typeConverter.convert(adminUserUpdateInfoDto, AdminUser.class);
        //更新数据库
        baseMapper.updateById(adminUser);
        adminUser = baseMapper.selectById(adminUser);
        SessionUtil.setUser(adminUser);
        return Result.ok(typeConverter.convert(adminUser, AdminUserVo.class));
    }

    @Override
    public Result<String> updateUserPassword(AdminUserUpdatePasswordDto adminUserUpdatePasswordDto) {
        if (adminUserUpdatePasswordDto.getPassword().equals(adminUserUpdatePasswordDto.getConfirmPassword())) {
            if (PasswdStrength.getLevel(adminUserUpdatePasswordDto.getPassword()).equals(PasswdStrength.PASSWD_LEVEL.EASY)) {
                return Result.error("密码过于简单");
            }
            Long userId = SessionUtil.getUserId();
            AdminUser adminUser = getById(userId);
            String newPassword = secureService.encryptHex(adminUserUpdatePasswordDto.getPassword());
            if (newPassword.equals(adminUser.getPassword())) {
                return Result.error("新密码不能与旧密码相同");
            }
            adminUser.setPassword(newPassword);
            baseMapper.updateById(adminUser);
            //使之前的token失效
            StpUtil.kickout(adminUser.getId());
            StpUtil.login(adminUser.getId());
            SessionUtil.setUser(adminUser);
            log.debug("密码修改成功[{}]", adminUser.getUsername());
            return Result.ok(StpUtil.getTokenInfo().getTokenValue());
        } else {
            return Result.error("两次密码不一致");
        }
    }

    /**
     * 校验用户名规则
     *
     * @param username 用户名
     * @return
     */
    private Boolean validateUsernameRule(String username) {
        return Validator.isGeneral(username, 5, 32);
    }

    @Override
    public Result<Boolean> validateUsername(String username) {
        boolean general = validateUsernameRule(username);
        if (!general) {
            throw new JsonException("用户名格式不符合规则");
        } else {
            AdminUser adminUser = findByUsername(username);
            return Result.ok(adminUser == null);
        }
    }

    @Override
    @Transactional
    public void initUsers(Set<GlobalProperties.UserConfig> userConfigs) {
        long count = count();
        if (count == 0) {
            log.debug("开始初始化用户账户");
            List<AdminUser> adminUsers = userConfigs.stream().map(
                    userConfig -> {
                        AdminUser adminUser = new AdminUser();
                        adminUser.setUsername(userConfig.getUsername());
                        adminUser.setNickname(userConfig.getUsername());
                        String newPassword = secureService.encryptHex(userConfig.getPassword());
                        adminUser.setPassword(newPassword);
                        return adminUser;
                    }
            ).toList();
            saveBatch(adminUsers);
        } else {
            log.debug("当前系统已经存在用户账户，不再初始化用户");
        }
    }

    @Override
    public Result<PageResult<AdminUserVo>> findByCondition(AdminUserQueryDto adminUserQueryDto) {
        PageResult<AdminUserVo> page = lambdaJoinWrapper()
                .like(StrUtil.isNotBlank(adminUserQueryDto.getUsername()), AdminUser::getUsername, adminUserQueryDto.getUsername())
                .like(StrUtil.isNotBlank(adminUserQueryDto.getNickname()), AdminUser::getNickname, adminUserQueryDto.getNickname())
                .orderByDesc(AdminUser::getUpdateTime)
                .orderByDesc(AdminUser::getCreateTime)
                .page(adminUserQueryDto.toPage(), AdminUserVo.class);
        return Result.ok(page);
    }

    @Override
    public Result<String> logout() {
        StpUtil.logout();
        return Result.ok();
    }

}
